package allinpay.yun.demo;

import allinpay.yun.demo.dto.request.Req;
import allinpay.yun.demo.dto.request.Request;
import allinpay.yun.demo.dto.request.member.CreateMemberReq;
import allinpay.yun.demo.dto.request.member.CreateMemberReqParam;
import allinpay.yun.demo.dto.response.Response;
import allinpay.yun.demo.dto.response.returnvalue.member.CreateMemberReturnValue;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import ime.service.client.SOAClient;
import ime.service.util.RSAUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.Consts;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author: lijin
 * @date: 2018年11月21日
 */

@Slf4j
public class AllinpayClient {
    //商户号
    private String sysid = "100009001000";
    //密钥密码
    static String pwd = "900724";
    //证书名称
    static String alias = "100009001000";
    //证书文件路径，用户指定
    static String path = "cert/100009001000.pfx";
    //加密方法
    private String signMethod = "SHA1WithRSA";

    private String version = "1.0";

    private String ssoid = "ime_public_ssoid";

    //SOA接口地址
    private String serverAddress = "http://116.228.64.55:9092/service/soa";

    private RestTemplate restTemplate = new RestTemplate();

    private ObjectMapper objectMapper = new ObjectMapper();

    private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private PrivateKey privateKey;
    private PublicKey publicKey;

    public AllinpayClient() throws Exception {

        URL resource = AllinpayClient.class.getClassLoader().getResource(path);
        path = resource.getPath();
        publicKey = RSAUtil.loadPublicKey(alias, path, pwd);
        privateKey = RSAUtil.loadPrivateKey(alias, path, pwd);

        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

    }

    //RSA加密
    public String rsaEncrypt(String signStr) throws Exception {
        try {

            RSAUtil rsaUtil = new RSAUtil((RSAPublicKey) publicKey, (RSAPrivateKey) privateKey);
            return rsaUtil.encrypt(signStr);
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }


    public MultiValueMap<String, String> buildRequestParam(Req req) throws Exception {
        String reqStr = objectMapper.writeValueAsString(req);
        LinkedMultiValueMap<String, String> multiValueMap = new LinkedMultiValueMap<>();
        multiValueMap.add("sysid", sysid);
        multiValueMap.add("v", version);
        multiValueMap.add("ssoid", ssoid);

        String timestamp = this.format.format(new Date());
        multiValueMap.add("timestamp", timestamp);
        multiValueMap.add("req", reqStr);
        multiValueMap.add("sign", sign(sysid, reqStr, timestamp));

        return multiValueMap;
    }

    public String sign(String sysid, String req, String timestamp) throws Exception {
        StringBuilder sb = new StringBuilder();
        sb.append(sysid).append(req).append(timestamp);

        return RSAUtil.sign(this.privateKey, sb.toString());
    }

    public void verifySign(Response response) throws Exception {
        String signedValue = response.getSignedValue();
        String sign = response.getSign();
        if (!RSAUtil.verify(this.publicKey, signedValue, sign)) throw new Exception("响应签名验证错误");
    }


    public <T> Response<T> execute(Req req, Class<T> returnValueClazz) throws Exception {
        MultiValueMap<String, String> requestParam = buildRequestParam(req);
        String responseStr = restTemplate.postForObject(serverAddress, requestParam, String.class);
        log.debug("通联支付返回：{}", responseStr);
        Response<T> response = objectMapper.readValue(responseStr, Response.class);
        verifySign(response);

        String signedValue = response.getSignedValue();
        if (response.isOk())  response.setReturnValue(objectMapper.readValue(signedValue, returnValueClazz));
        return response;
    }


    public String getSysid() {
        return sysid;
    }

    public void setSysid(String sysid) {
        this.sysid = sysid;
    }

    public static String getPwd() {
        return pwd;
    }

    public static void setPwd(String pwd) {
        AllinpayClient.pwd = pwd;
    }

    public static String getAlias() {
        return alias;
    }

    public static void setAlias(String alias) {
        AllinpayClient.alias = alias;
    }

    public static String getPath() {
        return path;
    }

    public static void setPath(String path) {
        AllinpayClient.path = path;
    }

    public String getSignMethod() {
        return signMethod;
    }

    public void setSignMethod(String signMethod) {
        this.signMethod = signMethod;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public String getServerAddress() {
        return serverAddress;
    }

    public void setServerAddress(String serverAddress) {
        this.serverAddress = serverAddress;
    }

    public RestTemplate getRestTemplate() {
        return restTemplate;
    }

    public void setRestTemplate(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    public ObjectMapper getObjectMapper() {
        return objectMapper;
    }

    public void setObjectMapper(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    public SimpleDateFormat getFormat() {
        return format;
    }

    public void setFormat(SimpleDateFormat format) {
        this.format = format;
    }

    public PrivateKey getPrivateKey() {
        return privateKey;
    }

    public PublicKey getPublicKey() {
        return publicKey;
    }
}
